package com.dianjing.dianjing.controller;

import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.servlet.mvc.method.RequestMappingInfo;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;

import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.OutputStream;
import java.lang.reflect.Method;
import java.text.SimpleDateFormat;
import java.util.*;

@RestController
@Api(tags = "上传与下载模块")
public class UploadController {
    /**
     * 使用看 WINDOWS_FLAG 的值是否是 System.getProperty("os.name") 的子串（忽略大小写）
     */
    private static String WINDOWS_FLAG = "WINDOW";
    /**
     * 如果是Windows在哪个盘符下查找，默认是“C:”，即C盘
     */
    @Value("${upload.win-drive-letter:C:}")
    private String winDriveLetter;

    /**
     * 在Linux中的直接应用地址，在Windows中需要在前面加上 winDriveLetter才是资源保存的位置
     */
    @Value("${upload.linux-path}")
    private String linuxPath;
//    /**
//     * 默认false，即默认是http而不是https
//     */
//    @Value("${upload.https:false}")
//    private boolean isHttps;

    @Value("${upload.host}")
    private String host;

    @Value("${upload.port:-1}")
    private Integer port;
    /**
     * 网站统一前缀（默认就是""空字符串）
     */
    @Value("${server.servlet.context-path:}")
    private String prefix;

    @Value("${server.port}")
    private int serverPort;



    @Resource
    private RequestMappingHandlerMapping requestMappingHandlerMapping;

    /**
     * 用于动态注册请求
     * 项目启动后，会自动执行被@PostConstruct修饰的方法
     */
    @PostConstruct
    public void registryUploadRequestMapping() {
        System.out.println("执行了" + requestMappingHandlerMapping);
        // 指定URL
        RequestMappingInfo requestMappingInfo = RequestMappingInfo.paths(linuxPath + "/**").methods(RequestMethod.GET).build();
        try {
            // 指定URL对应的Method
            Method targetMethod = this.getClass().getDeclaredMethod("responseUploadResource", HttpServletRequest.class, HttpServletResponse.class);
            // 注册 RequestMapping
            requestMappingHandlerMapping.registerMapping(requestMappingInfo, this, targetMethod);
        } catch (NoSuchMethodException e) {
            System.out.println("[ERROR] 注册UploadRequestMapping失败，文件请求功能将无法使用！");
            e.printStackTrace();
        }
    }


    private static Map<String, String> typeSuffixMap = new HashMap<>(16);

    static {
        // 图片类型
        typeSuffixMap.put(".jpg", "image/jpeg");
        typeSuffixMap.put(".gif", "image/gif");
        typeSuffixMap.put(".png", "image/png");
        // 音频类型
        typeSuffixMap.put(".mp3", "audio/mpeg");
        // 视频类型
        typeSuffixMap.put(".mp4", "video/mp4");
        typeSuffixMap.put(".avi", "video/x-msvideo");
        // 其它类型都以二进制流的方式 application/octet-stream
    }

    @ApiOperation("文件响应")
    public void responseUploadResource(HttpServletRequest req, HttpServletResponse res) {
        try {
            // 如果当前是Win系统,读取的盘符（Linux系统没有盘符的概念，是空串）
            String currentDL = "";
            if (isWindows()) {
                currentDL = winDriveLetter;
            }
            // 得到存放路径("/dj/upload")后面的路径比如  http://localhost:8089/dj/upload/2022-12-1/java.jpg childPath就是 /2022-12-1/java.jpg
            String childPath = req.getServletPath().split(linuxPath)[1];
            // 拼成保存在本地的存储路径，如 /dj/upload/2022-12-1/java.jpg （如果winDriveLetter是“C:”,那Win下就是:   C:/dj/upload/2022-12-1/java.jpg）
            String path = currentDL + linuxPath + childPath;
            File file = new File(path);
            FileInputStream fis = new FileInputStream(file);
            byte[] buffer = new byte[1024];
            int len = 0;
            // 读取请求后缀
            String suffix = path.substring(path.lastIndexOf("."), path.length());
            String responseType = fileSuffixSwitchResponseType(suffix);
            res.setContentType(fileSuffixSwitchResponseType(responseType));
            OutputStream os = res.getOutputStream();
            while ((len = fis.read(buffer)) > 0) {
                os.write(buffer, 0, len);
            }
            fis.close();
            os.close();
        } catch (Exception e) {
            e.printStackTrace();
        }

    }

    public String MultipartFileToUrl(MultipartFile file) {

        try {
            // 理论上需要判断用户是否为画师，但问题不大，就算普通用户模拟上传了，也看不到
            if (file != null) {
                // 上传作品 -->
                // 原始名称
                String originalFilename = file.getOriginalFilename();
                // 扩展名
                String ext = originalFilename.substring(originalFilename.lastIndexOf("."), originalFilename.length());
                String fullFileName = UUID.randomUUID().toString().toUpperCase().replaceAll("-", "") + ext;
                // 存储,要存储到指定的文件夹，/uploads/yyyy-MM-dd，考虑文件过多的情况按照日期，生成一个子文件夹
                String fileBox = null;
                fileBox = isWindows() ? (winDriveLetter + linuxPath) : linuxPath;
                String childDir = new SimpleDateFormat("yyyy/MM/dd").format(new Date());
                File folder = new File(fileBox + "/" + childDir);
                // 确保目录存在
                if (!folder.exists()) {
                    folder.mkdirs();
                }
                // 存储文件到目录
                file.transferTo(new File(folder, fullFileName));
                // 存放在数据库中的url
                String imgUrl = (isHttps() ? "https" : "http") + "://" + getHost() + ":" + getPort() + prefix + linuxPath + "/" + childDir + "/" + fullFileName;
                return imgUrl;
            }
        } catch (Exception e) {
            System.out.println("ERROR 上传失败：" + e.getMessage());
        }
        return null;
    }

    /**
     * 判断当前是否为Windows
     *
     * @return
     */
    private static boolean isWindows() {
        return System.getProperty("os.name").toUpperCase().indexOf(WINDOWS_FLAG) >= 0;
    }

    private static HttpServletRequest getRequestObj() {
        HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
        return request;
    }
    /**
     * 判断是否为https请求
     * @return
     */
    private static boolean isHttps() {
        return  getRequestObj().getRequestURL().indexOf("https") == 0 ;
    }

    /**
     * 根据文件后缀返回文件类型
     */
    private static String fileSuffixSwitchResponseType(String fileSuffix) {
        fileSuffix = fileSuffix.trim();
        // 对照表进行获取
        Set<Map.Entry<String, String>> entries = typeSuffixMap.entrySet();
        for (Map.Entry<String, String> entry : entries) {
            String suffix = entry.getKey();
            String type = entry.getValue();
            if (suffix.equals(fileSuffix)) {
                return type;
            }
        }
        // 到这里是没有找到的,统一返回“二进制流”
        return "application/octet-stream";
    }

    /**
     * 将host变量进行包装，如果没有配置host就从url中获取
     * @return
     */
    private String getHost() {

        if (host == null || "".equals(host)) {
            // 从请求中获取
            // 下面这种也是IP
            String EQ_LOCALHOST_IP_OF_IP = "0:0:0:0:0:0:0:1";
            String remoteHost = getRequestObj().getRemoteHost();
            if (EQ_LOCALHOST_IP_OF_IP.equals(remoteHost)) {
                remoteHost = "127.0.0.1";
            }
            return remoteHost;
        }
        return host;
    }

    private Integer getPort() {
        return port == -1?serverPort:port;
    }

}

